/*
 * Copyright (C) 2009-2012 Geometer Plus <contact@geometerplus.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.geometerplus.fbreader.book;

import org.geometerplus.fbreader.library.BooksDatabase;
import org.geometerplus.zlibrary.text.view.ZLTextElement;
import org.geometerplus.zlibrary.text.view.ZLTextFixedPosition;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
import org.geometerplus.zlibrary.text.view.ZLTextWord;
import org.geometerplus.zlibrary.text.view.ZLTextWordCursor;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

public final class Bookmark extends ZLTextFixedPosition {
    public final static int CREATION = 0;
    public final static int MODIFICATION = 1;
    public final static int ACCESS = 2;
    public final static int LATEST = 3;

    public static List<Bookmark> bookmarks() {
        return BooksDatabase.Instance().loadAllVisibleBookmarks();
    }

    public static List<Bookmark> invisibleBookmarks(ReadBook book) {
        final List<Bookmark> list = BooksDatabase.Instance().loadBookmarks(book.getId(), false);
        Collections.sort(list, new ByTimeComparator());
        return list;
    }

    private long myId;
    private final long myBookId;
    private final String myBookTitle;
    private String myText;
    private final Date myCreationDate;
    private Date myModificationDate;
    private Date myAccessDate;
    private int myAccessCount;
    private Date myLatestDate;

    public final String ModelId;
    public final boolean IsVisible;

    private boolean myIsChanged;


    private int myLength;
    private int myStyleId;


    private ZLTextFixedPosition myEnd;

    public Bookmark(long id, long bookId, String bookTitle, String text, Date creationDate, Date modificationDate, Date accessDate, int accessCount, String modelId, int paragraphIndex, int elementIndex, int charIndex, boolean isVisible, String tag) {
        super(paragraphIndex, elementIndex, charIndex);

        myId = id;
        myBookId = bookId;
        myBookTitle = bookTitle;
        myText = text;
        myCreationDate = creationDate;
        myModificationDate = modificationDate;
        myLatestDate = (modificationDate != null) ? modificationDate : creationDate;
        if (accessDate != null) {
            myAccessDate = accessDate;
            if (myLatestDate.compareTo(accessDate) < 0) {
                myLatestDate = accessDate;
            }
        }
        myAccessCount = accessCount;
        ModelId = modelId;
        IsVisible = isVisible;
        myIsChanged = false;
        setTag(tag);
    }
    public Bookmark(ReadBook book, String modelId, ZLTextWordCursor cursor, boolean isVisible, int maxLength) {
        this(book, modelId, cursor, createBookmarkText(cursor, maxLength), isVisible, null);
    }
    public Bookmark(ReadBook book, String modelId, ZLTextWordCursor cursor, int maxLength, boolean isVisible, String title) {
        this(book, modelId, cursor, createBookmarkText(cursor, maxLength), isVisible, title);
    }
    public Bookmark(ReadBook book, String modelId, ZLTextPosition position, String text, boolean isVisible)
    {
        this(book, modelId, position, text, isVisible, null);
    }
    public Bookmark(ReadBook book, String modelId, ZLTextPosition position, String text, boolean isVisible, String title) {
        super(position);

        myId = -1;
        myBookId = book.getId();
        myBookTitle = title != null ? title : book.getTitle();
        myText = text;
        myCreationDate = new Date();
        ModelId = modelId;
        IsVisible = isVisible;
        myIsChanged = true;
    }

    public long getId() {
        return myId;
    }

    public long getBookId() {
        return myBookId;
    }

    public String getText() {
        return myText;
    }

    public String getBookTitle() {
        return myBookTitle;
    }

    public Date getTime(int timeStamp) {
        switch (timeStamp) {
            default:
            case CREATION:
                return myCreationDate;
            case MODIFICATION:
                return myModificationDate;
            case ACCESS:
                return myAccessDate;
            case LATEST:
                return myLatestDate;
        }
    }

    public Date getTime()
    {
        return myCreationDate;
    }
    public int getAccessCount() {
        return myAccessCount;
    }

    public void setText(String text) {
        if (!text.equals(myText)) {
            myText = text;
            myModificationDate = new Date();
            myLatestDate = myModificationDate;
            myIsChanged = true;
        }
    }

    public void onOpen() {
        myAccessDate = new Date();
        ++myAccessCount;
        myLatestDate = myAccessDate;
        myIsChanged = true;
    }

    public void save() {
        if (myIsChanged) {
            myId = BooksDatabase.Instance().saveBookmark(this);
            myIsChanged = false;
        }
    }

    public void delete() {
        if (myId != -1) {
            BooksDatabase.Instance().deleteBookmark(this);
        }
    }

    public static class ByTimeComparator implements Comparator<Bookmark> {
        public int compare(Bookmark bm0, Bookmark bm1) {
            return bm1.getTime(LATEST).compareTo(bm0.getTime(LATEST));
        }
    }

    public static String createBookmarkText(ZLTextWordCursor cursor, int maxWords) {
        if(cursor == null || cursor.isNull())
        {
            return "";
        }
        cursor = new ZLTextWordCursor(cursor);

        final StringBuilder builder = new StringBuilder();
        final StringBuilder sentenceBuilder = new StringBuilder();
        final StringBuilder phraseBuilder = new StringBuilder();

        int wordCounter = 0;
        int sentenceCounter = 0;
        int storedWordCounter = 0;
        boolean lineIsNonEmpty = false;
        boolean appendLineBreak = false;
        mainLoop:
        while (wordCounter < maxWords && sentenceCounter < 3) {
            while (cursor.isEndOfParagraph()) {
                if (!cursor.nextParagraph()) {
                    break mainLoop;
                }
                if ((builder.length() > 0) && cursor.getParagraphCursor().isEndOfSection()) {
                    break mainLoop;
                }
                if (phraseBuilder.length() > 0) {
                    sentenceBuilder.append(phraseBuilder);
                    phraseBuilder.delete(0, phraseBuilder.length());
                }
                if (sentenceBuilder.length() > 0) {
                    if (appendLineBreak) {
                        builder.append("\n");
                    }
                    builder.append(sentenceBuilder);
                    sentenceBuilder.delete(0, sentenceBuilder.length());
                    ++sentenceCounter;
                    storedWordCounter = wordCounter;
                }
                lineIsNonEmpty = false;
                if (builder.length() > 0) {
                    appendLineBreak = true;
                }
            }
            final ZLTextElement element = cursor.getElement();
            if (element != null && element instanceof ZLTextWord)
            {
                final ZLTextWord word = (ZLTextWord)element;
//				if (lineIsNonEmpty) {
//					phraseBuilder.append("");
//				}
                phraseBuilder.append(word.Data, word.Offset, word.Length);
                ++wordCounter;
                lineIsNonEmpty = true;
                switch (word.Data[word.Offset + word.Length - 1]) {
                    case ',':
                    case ':':
                    case ';':
                    case ')':
                        sentenceBuilder.append(phraseBuilder);
                        phraseBuilder.delete(0, phraseBuilder.length());
                        break;
                    case '.':
                    case '!':
                    case '?':
                        ++sentenceCounter;
                        if (appendLineBreak) {
                            builder.append("\n");
                            appendLineBreak = false;
                        }
                        sentenceBuilder.append(phraseBuilder);
                        phraseBuilder.delete(0, phraseBuilder.length());
                        builder.append(sentenceBuilder);
                        sentenceBuilder.delete(0, sentenceBuilder.length());
                        storedWordCounter = wordCounter;
                        break;
                }
            }
            cursor.nextWord();
        }
        if (storedWordCounter < 4) {
            if (sentenceBuilder.length() == 0) {
                sentenceBuilder.append(phraseBuilder);
            }
            if (appendLineBreak) {
                builder.append("\n");
            }
            builder.append(sentenceBuilder);
        }
        return builder.toString();
    }
    @Override
    public void setTag(String tag) {

        super.setTag(tag);
        myIsChanged = true;
    }
    public ZLTextPosition getEnd() {
        return myEnd;
    }

    void setEnd(int paragraphsIndex, int elementIndex, int charIndex) {
        myEnd = new ZLTextFixedPosition(paragraphsIndex, elementIndex, charIndex);
    }

}
